\hypertarget{index_autotoc_md0}{}\doxysection{checkpoint1 实验文档}\label{index_autotoc_md0}
\begin{quote}
王耕宇 17307110209 \end{quote}
\mbox{[}toc\mbox{]}\hypertarget{index_autotoc_md1}{}\doxysubsection{编程思想}\label{index_autotoc_md1}
程序主要利用{\ttfamily sys/socket.\+h}头文件，通过调用\+TCP套接字，以达到文件传输协议的实践效果。\hypertarget{index_autotoc_md2}{}\doxysubsubsection{1、程序结构}\label{index_autotoc_md2}
根据\+RFC959协议，\+FTP有如下服务模型：



根据\+PJ的具体要求以及分工的考虑，本次代码主要包含4个文件，{\ttfamily \mbox{\hyperlink{ftp__client_8c}{ftp\+\_\+client.\+c}}}，{\ttfamily \mbox{\hyperlink{ftp__server_8c}{ftp\+\_\+server.\+c}}}主要用来实现套接字的控制连接、数据连接以及通过main函数调用命令接口。{\ttfamily \mbox{\hyperlink{cmd__client_8c}{cmd\+\_\+client.\+c}}}、{\ttfamily \mbox{\hyperlink{cmd__server_8c}{cmd\+\_\+server.\+c}}}则用于分别实现客户端和服务端的命令处理。\hypertarget{index_autotoc_md3}{}\doxysubsubsection{2、工作模式}\label{index_autotoc_md3}
FTP 客户端和客户端必须建立不同于控制连接的新的连接用来传送数据。客户端和服务端建立数据连接时，需要考虑连接建立的方式，称为工作模式。由于数据连接不需要全程都要存在，因此在本次\+PJ中，数据连接主要在实现{\ttfamily ls}、{\ttfamily put}、{\ttfamily get}命令时建立。\+FTP支持两种模式：主动模式（\+PORT）和被动模式（\+PASV）。在本次\+PJ中，小组对以上两个模式均进行了实践。二者在控制连接过程中方式相同，均是客户端向服务器21端口发送连接请求，但在数据连接时有所不同。


\begin{DoxyItemize}
\item 主动模式，本次程序默认模式。在主动模式下，客户端需要接收数据的时候在控制连接通道上发送\+PORT命令，并进入监听等待连接。服务器端接收到该命令后，通过自己的\+TCP 20端口与客户端的指定端口建立连接，发送数据。这种方式下是服务器向客户端建立数据连接。其中，客户端数据连接的端口号是大于1024的临时端口，而\+FTP服务器的数据端口为20。
\item 被动模式，本次程序中使用{\ttfamily mode}命令可将工作模式切换为被动模式。\+Passive模式在建立数据通道时发送\+Pasv命令。根据\+RFC959协议，服务端控制进程收到被动连接请求后，将随机打开随机一个临时端口，并通过返回{\ttfamily 227 entering passive mode(h1,h2,h3,h4,p1,p2)}信息，将端口号告知客户端控制进程，其中端口号是 \$p1$\ast$256+p2\$，ip 地址为{\ttfamily h1.\+h2.\+h3.\+h4}；客户端连接\+FTP服务器此端口，然后\+FTP服务器将通过这个端口进行数据的传送，这种情况下的连接请求是由客户端向服务器发起的。
\end{DoxyItemize}

当数据传输完毕，服务端数据传输进程返回传输完毕，数据连接通道会关闭，只保留控制连接通道。\hypertarget{index_autotoc_md4}{}\doxysubsubsection{3、数据传输}\label{index_autotoc_md4}
在\+RFC959协议中，\+FTP主要有三种传输模式：流模式、块模式以及压缩模式。相比于其他两种模式，流模式具有效率高，实现简单，传输数据复杂性隔离的特点，因此本次\+PJ数据传输主要使用流模式。具体实践中，主要依靠{\ttfamily write}函数向socket的文件句柄写入。

对于\+ASCII码和二进制两种文本形式，主要依靠用户在使用{\ttfamily get}、{\ttfamily put}函数时进行询问，根据用户的输入选择不同的文件打开方式以及流的读取方式。值得注意的是，只需在文件写入时进行判断，文件全部以字节流的形式进行读取。\hypertarget{index_autotoc_md5}{}\doxysubsubsection{4、访问控制}\label{index_autotoc_md5}
由于本次pj没有并发访问等要求，因此对服务端21端口的最大监听数设置为1。对于用户的访问控制，我们在服务端存储了用户名为{\ttfamily user}，密码为{\ttfamily 123}的账号，该用户拥有所有命令的操作权限，同时我们设置了匿名用户，用户名为{\ttfamily anonymous}，该用户登录不需要密码，但不具有{\ttfamily get}，{\ttfamily put}命令的操作权限。\hypertarget{index_autotoc_md6}{}\doxysubsubsection{5、\+FTP命令}\label{index_autotoc_md6}
本次\+PJ中完整实现的服务端\+RFC959命令接口有：

{\ttfamily USER}，{\ttfamily PASS}，{\ttfamily CWD}，{\ttfamily QUIT}，{\ttfamily PORT}，{\ttfamily PASV}，{\ttfamily RETR}，{\ttfamily STOR}，{\ttfamily DELE}，{\ttfamily RMD}，{\ttfamily MKD}，{\ttfamily PWD}，{\ttfamily LIST}

除了上述命令，为了在事件过程中达到更好的效果，还非正式地实现了传递、获取文件大小的{\ttfamily SIZE}指令。

对于客户端窗口，除了\+PJ文档中规定的客户端命令，我们还实现了部分本地操作命令，包括{\ttfamily lls}，{\ttfamily lcd}，{\ttfamily lpwd}，以便于本地用户的操作。\hypertarget{index_autotoc_md7}{}\doxysubsubsection{6、\+FTP响应}\label{index_autotoc_md7}
根据\+RFC959中规定：

\begin{quote}
The general form of a reply is a completion code(including error codes) followed by a text string. The codes are for use by programs and the text is usually intended for human users. \end{quote}
所有响应包含完成码和文本字符串，在本次\+PJ中，我们小组最初实施了此方案，但是由于response太多会干扰用户正常操作，同时可能污染控制信道，增加处理复杂程度，因此对于\+RFC959协议中规定的响应信息，我们只选择性地实施了部分，多数用于服务器程序的错误提示，在正常操作中不会出现，以提供更好的使用体验。\hypertarget{index_autotoc_md8}{}\doxysubsection{代码分析}\label{index_autotoc_md8}
为了保持代码的简洁易读，我们删掉了部分消息处理、异常处理代码，只保留关键流程。由于封装了不同的层次，下面代码可能存在于不同函数中。\hypertarget{index_autotoc_md9}{}\doxysubsubsection{1、控制连接}\label{index_autotoc_md9}
{\bfseries{客户端}}


\begin{DoxyCode}{0}
\DoxyCodeLine{\textcolor{comment}{//ftp\_client.c : fill\_host\_addr}}
\DoxyCodeLine{\textcolor{comment}{//设置服务器信息}}
\DoxyCodeLine{\textcolor{keyword}{struct }sockaddr\_in host;}
\DoxyCodeLine{bzero(host, \textcolor{keyword}{sizeof}(\textcolor{keyword}{struct} sockaddr\_in));}
\DoxyCodeLine{host-\/>sin\_family = AF\_INET;     \textcolor{comment}{//使用IPv4}}
\DoxyCodeLine{host-\/>sin\_addr.s\_addr = inet\_addr(host\_ip\_addr);    \textcolor{comment}{//服务器ip}}
\DoxyCodeLine{host-\/>sin\_port = htons(21);     \textcolor{comment}{//服务器控制连接端口}}
\DoxyCodeLine{}
\DoxyCodeLine{\textcolor{comment}{//ftp\_client.c : xconnect}}
\DoxyCodeLine{\textcolor{comment}{//向服务器发送连接请求}}
\DoxyCodeLine{\textcolor{keyword}{struct }timeval outtime;}
\DoxyCodeLine{outtime.tv\_sec = 3; outtime.tv\_usec = 0;}
\DoxyCodeLine{\textcolor{keywordtype}{int} s = socket(AF\_INET, SOCK\_STREAM, 0);        \textcolor{comment}{//创建TCP类型socket}}
\DoxyCodeLine{\textcolor{keywordtype}{int} set = setsockopt(s, SOL\_SOCKET, SO\_RCVTIMEO, \&outtime, \textcolor{keyword}{sizeof}(outtime)); \textcolor{comment}{//设置超时时间}}
\DoxyCodeLine{connect(s, (\textcolor{keyword}{struct} sockaddr *)s\_addr, \textcolor{keyword}{sizeof}(\textcolor{keyword}{struct} sockaddr\_in));  \textcolor{comment}{//向控制器发送连接请求}}
\DoxyCodeLine{\textcolor{keywordflow}{return} s;}

\end{DoxyCode}


{\bfseries{服务端}}


\begin{DoxyCode}{0}
\DoxyCodeLine{\textcolor{comment}{//ftp\_server.c : create\_socket}}
\DoxyCodeLine{\textcolor{keywordtype}{int} sockfd = socket(AF\_INET, SOCK\_STREAM, 0);}
\DoxyCodeLine{}
\DoxyCodeLine{\textcolor{comment}{//ftp\_server.c : bind\_to\_port}}
\DoxyCodeLine{\textcolor{comment}{//将套接字绑定到该主机21端口}}
\DoxyCodeLine{bzero(\&server\_addr, \textcolor{keyword}{sizeof}(server\_addr));}
\DoxyCodeLine{server\_addr.sin\_family = AF\_INET;                }
\DoxyCodeLine{server\_addr.sin\_port = htons(FTP\_SERVER\_PORT);\textcolor{comment}{// 设置21端口}}
\DoxyCodeLine{server\_addr.sin\_addr.s\_addr = htonl(INADDR\_ANY);}
\DoxyCodeLine{\textcolor{keywordtype}{int} opt = 1;}
\DoxyCodeLine{setsockopt(sockfd, SOL\_SOCKET, SO\_REUSEPORT, \&opt, \textcolor{keyword}{sizeof}(\textcolor{keywordtype}{int}));}
\DoxyCodeLine{bind(sockfd, (\textcolor{keyword}{struct} sockaddr *)\&server\_addr, \textcolor{keyword}{sizeof}(server\_addr));}
\DoxyCodeLine{}
\DoxyCodeLine{\textcolor{comment}{//ftp\_server.c : listen\_to\_port}}
\DoxyCodeLine{\textcolor{comment}{//监听21端口，等待连接}}
\DoxyCodeLine{\textcolor{keywordflow}{if} ((listen(sockfd, backlog)) == -\/1)\{}
\DoxyCodeLine{    perror(\textcolor{stringliteral}{"{}Error in listening"{}}); exit(1);}
\DoxyCodeLine{\}}
\DoxyCodeLine{}
\DoxyCodeLine{\textcolor{comment}{//ftp\_server.c : accept\_connections}}
\DoxyCodeLine{\textcolor{comment}{//获取已连接套接字}}
\DoxyCodeLine{\textcolor{keyword}{struct }sockaddr\_in client\_addr;}
\DoxyCodeLine{socklen\_t len = \textcolor{keyword}{sizeof}(client\_addr);}
\DoxyCodeLine{\textcolor{keywordtype}{int} sockfd = accept(sock\_listen, (\textcolor{keyword}{struct} sockaddr *)\&client\_addr, \&len);}
\DoxyCodeLine{\textcolor{keywordflow}{return} sockfd;}

\end{DoxyCode}
\hypertarget{index_autotoc_md10}{}\doxysubsubsection{2、\+PORT数据连接}\label{index_autotoc_md10}
{\bfseries{客户端}}


\begin{DoxyCode}{0}
\DoxyCodeLine{\textcolor{comment}{//ftp\_client.c : xconnect\_ftpdata}}
\DoxyCodeLine{\textcolor{comment}{//创建socket}}
\DoxyCodeLine{\textcolor{keywordtype}{int} get\_sock = socket(AF\_INET, SOCK\_STREAM, 0);;}
\DoxyCodeLine{\textcolor{comment}{//设置随机端口}}
\DoxyCodeLine{\textcolor{keywordtype}{int} client\_port = \mbox{\hyperlink{ftp__client_8c_a0d2d0ee5a8e9a4e4a5f215b52d7e73c2}{rand\_local\_port}}();}
\DoxyCodeLine{bzero(\&local\_host, \textcolor{keyword}{sizeof}(local\_host));}
\DoxyCodeLine{local\_host.sin\_family = AF\_INET;}
\DoxyCodeLine{local\_host.sin\_port = htons(client\_port);}
\DoxyCodeLine{local\_host.sin\_addr.s\_addr = htonl(INADDR\_ANY);}
\DoxyCodeLine{}
\DoxyCodeLine{\textcolor{comment}{//绑定本地端口}}
\DoxyCodeLine{set = bind(get\_sock, (\textcolor{keyword}{struct} sockaddr *)\&local\_host, \textcolor{keyword}{sizeof}(local\_host));}
\DoxyCodeLine{}
\DoxyCodeLine{\textcolor{comment}{//监听scoket}}
\DoxyCodeLine{set = listen(get\_sock, 1);}
\DoxyCodeLine{}
\DoxyCodeLine{\textcolor{comment}{//获取本地ip}}
\DoxyCodeLine{\textcolor{keywordflow}{if} (getsockname(sock\_control, (\textcolor{keyword}{struct} sockaddr *)\&local, (socklen\_t *)\&addr\_len) < 0)}
\DoxyCodeLine{    \textcolor{keywordflow}{return};}
\DoxyCodeLine{snprintf(local\_ip, \textcolor{keyword}{sizeof}(local\_ip), \textcolor{stringliteral}{"{}\%s"{}}, inet\_ntoa(local.sin\_addr));}
\DoxyCodeLine{local\_ip[strlen(local\_ip)] = \textcolor{charliteral}{'\(\backslash\)0'}; ip\_1 = local\_ip;}
\DoxyCodeLine{ip\_2 = strchr(local\_ip, \textcolor{charliteral}{'.'}); *ip\_2 = \textcolor{charliteral}{'\(\backslash\)0'}; ip\_2++;}
\DoxyCodeLine{ip\_3 = strchr(ip\_2, \textcolor{charliteral}{'.'}); *ip\_3 = \textcolor{charliteral}{'\(\backslash\)0'}; ip\_3++;}
\DoxyCodeLine{ip\_4 = strchr(ip\_3, \textcolor{charliteral}{'.'}); *ip\_4 = \textcolor{charliteral}{'\(\backslash\)0'}; ip\_4++;}
\DoxyCodeLine{}
\DoxyCodeLine{\textcolor{comment}{//发送PORT命令}}
\DoxyCodeLine{snprintf(cmd\_buf, \textcolor{keyword}{sizeof}(cmd\_buf), \textcolor{stringliteral}{"{}PORT \%s, \%s, \%s, \%s, \%d, \%d"{}}, ip\_1, ip\_2, ip\_3, ip\_4, client\_port >> 8, client\_port \& 0xff);}
\DoxyCodeLine{\mbox{\hyperlink{ftp__client_8c_a3d3917e46b692a63525f64f0bb1c975d}{ftp\_send\_cmd}}(cmd\_buf, NULL, sock\_control);}
\DoxyCodeLine{}
\DoxyCodeLine{\textcolor{comment}{//等待连接响应}}
\DoxyCodeLine{\textcolor{keywordflow}{if} (\mbox{\hyperlink{ftp__client_8c_acc60139a882577df9182dec7cf130a95}{ftp\_get\_reply}}(sock\_control) != 200)\{}
\DoxyCodeLine{    printf(\textcolor{stringliteral}{"{}Can not use PORT mode! Please use \(\backslash\)"{}mode\(\backslash\)"{} change to PASV mode.\(\backslash\)n"{}});}
\DoxyCodeLine{    \mbox{\hyperlink{ftp__client_8c_ae82ce0c2ad9bca58caa399c290effbca}{cmd\_err\_exit}}(\textcolor{stringliteral}{"{}socket()"{}}, 1);}
\DoxyCodeLine{\} \textcolor{keywordflow}{else} \{}
\DoxyCodeLine{    \textcolor{comment}{//获取已连接数据socket}}
\DoxyCodeLine{    msg-\/>ftp\_data\_sock = accept(get\_sock, (\textcolor{keyword}{struct} sockaddr *)\&local\_host, (socklen\_t *)\&set);}
\DoxyCodeLine{\}}

\end{DoxyCode}


{\bfseries{服务端}}


\begin{DoxyCode}{0}
\DoxyCodeLine{\textcolor{comment}{//cmd\_server.c : handle\_port}}
\DoxyCodeLine{\textcolor{comment}{//获取PORT命令，接收控制端IP地址和数据端口号}}
\DoxyCodeLine{sscanf(msg-\/>control\_info, \textcolor{stringliteral}{"{}PORT \%d,\%d,\%d,\%d,\%d,\%d"{}}, \&act\_ip[0], \&act\_ip[1], \&act\_ip[2], \&act\_ip[3], (\textcolor{keywordtype}{int} *)\&act\_port[0], (\textcolor{keywordtype}{int} *)\&act\_port[1]);}
\DoxyCodeLine{sprintf(ip\_decimal, \textcolor{stringliteral}{"{}\%d.\%d.\%d.\%d"{}}, act\_ip[0], act\_ip[1], act\_ip[2], act\_ip[3]);}
\DoxyCodeLine{port\_dec = act\_port[0] * 256 + act\_port[1];}
\DoxyCodeLine{}
\DoxyCodeLine{addr\_data.sin\_family = AF\_INET; \textcolor{comment}{// local\_data\_addr\_act}}
\DoxyCodeLine{addr\_data.sin\_addr.s\_addr = inet\_addr(ip\_decimal);}
\DoxyCodeLine{addr\_data.sin\_port = htons(port\_dec);}
\DoxyCodeLine{}
\DoxyCodeLine{\textcolor{comment}{//绑定20端口}}
\DoxyCodeLine{msg-\/>ftp\_data\_sock = socket(AF\_INET, SOCK\_STREAM, 0);}
\DoxyCodeLine{server\_data.sin\_family = AF\_INET;}
\DoxyCodeLine{server\_data.sin\_addr.s\_addr = htonl(INADDR\_ANY);}
\DoxyCodeLine{server\_data.sin\_port = htons(20);}
\DoxyCodeLine{\textcolor{keywordtype}{int} set = bind(msg-\/>ftp\_data\_sock, (\textcolor{keyword}{struct} sockaddr *)\&server\_data, \textcolor{keyword}{sizeof}(server\_data));}
\DoxyCodeLine{}
\DoxyCodeLine{\textcolor{comment}{//服务器主动向客户端发送连接请求}}
\DoxyCodeLine{\textcolor{keywordflow}{if} (connect(msg-\/>ftp\_data\_sock, (\textcolor{keyword}{struct} sockaddr *)\&addr\_data, (\textcolor{keywordtype}{int})\textcolor{keyword}{sizeof}(\textcolor{keyword}{struct} sockaddr)) != 0)}
\DoxyCodeLine{\{}
\DoxyCodeLine{    \mbox{\hyperlink{ftp__server_8c_a80762bccbf172f3bcc3c6149c6c407c5}{send\_message}}(client\_sock, serverInfo425, \textcolor{stringliteral}{"{}\(\backslash\)0"{}});}
\DoxyCodeLine{    close(msg-\/>ftp\_data\_sock);}
\DoxyCodeLine{\} \textcolor{keywordflow}{else}}
\DoxyCodeLine{    \mbox{\hyperlink{ftp__server_8c_a80762bccbf172f3bcc3c6149c6c407c5}{send\_message}}(client\_sock, serverInfo200, \textcolor{stringliteral}{"{}\(\backslash\)0"{}});}

\end{DoxyCode}
\hypertarget{index_autotoc_md11}{}\doxysubsubsection{3、\+PASV数据连接}\label{index_autotoc_md11}
{\bfseries{客户端}}


\begin{DoxyCode}{0}
\DoxyCodeLine{\textcolor{comment}{//ftp\_client.c : get\_port}}
\DoxyCodeLine{\textcolor{comment}{//发送PASV命令并接收端口信息}}
\DoxyCodeLine{\mbox{\hyperlink{ftp__client_8c_a3d3917e46b692a63525f64f0bb1c975d}{ftp\_send\_cmd}}(\textcolor{stringliteral}{"{}PASV"{}}, NULL, sock\_control);}
\DoxyCodeLine{count = read(sock\_control, port\_respond, 510);}
\DoxyCodeLine{\textcolor{comment}{//提取端口信息}}
\DoxyCodeLine{\textcolor{keywordflow}{if} (atoi(port\_respond) == 227)}
\DoxyCodeLine{\{}
\DoxyCodeLine{    buf\_ptr = strrchr(port\_respond, \textcolor{charliteral}{','});}
\DoxyCodeLine{    port\_num = atoi(buf\_ptr + 1);}
\DoxyCodeLine{    buf\_ptr = strrchr(port\_respond, \textcolor{charliteral}{','});}
\DoxyCodeLine{    port\_num += atoi(buf\_ptr + 1) * 256;}
\DoxyCodeLine{    \textcolor{keywordflow}{return} port\_num;}
\DoxyCodeLine{\}}
\DoxyCodeLine{}
\DoxyCodeLine{\textcolor{comment}{//ftp\_client.c : xconnect}}
\DoxyCodeLine{\textcolor{comment}{//向服务器发送连接请求}}
\DoxyCodeLine{\textcolor{keyword}{struct }timeval outtime;}
\DoxyCodeLine{outtime.tv\_sec = 3; outtime.tv\_usec = 0;}
\DoxyCodeLine{\textcolor{keywordtype}{int} s = socket(AF\_INET, SOCK\_STREAM, 0);        \textcolor{comment}{//创建TCP类型socket}}
\DoxyCodeLine{\textcolor{keywordtype}{int} set = setsockopt(s, SOL\_SOCKET, SO\_RCVTIMEO, \&outtime, \textcolor{keyword}{sizeof}(outtime)); \textcolor{comment}{//设置超时时间}}
\DoxyCodeLine{connect(s, (\textcolor{keyword}{struct} sockaddr *)s\_addr, \textcolor{keyword}{sizeof}(\textcolor{keyword}{struct} sockaddr\_in));  \textcolor{comment}{//向控制器发送连接请求}}
\DoxyCodeLine{\textcolor{keywordflow}{return} s;}

\end{DoxyCode}


{\bfseries{服务端}}


\begin{DoxyCode}{0}
\DoxyCodeLine{\textcolor{comment}{//cmd\_server.c : create\_data\_sock}}
\DoxyCodeLine{\textcolor{comment}{//设置随机端口}}
\DoxyCodeLine{\textcolor{keyword}{struct }sockaddr\_in t\_data\_addr;}
\DoxyCodeLine{*sock = socket(AF\_INET, SOCK\_STREAM, 0);}
\DoxyCodeLine{srand((\textcolor{keywordtype}{int})time(0));}
\DoxyCodeLine{\textcolor{keywordtype}{int} a = rand() \% 1000 + 1025;}
\DoxyCodeLine{bzero(\&t\_data\_addr, \textcolor{keyword}{sizeof}(t\_data\_addr));}
\DoxyCodeLine{t\_data\_addr.sin\_family = AF\_INET;}
\DoxyCodeLine{t\_data\_addr.sin\_addr.s\_addr = htonl(INADDR\_ANY);}
\DoxyCodeLine{t\_data\_addr.sin\_port = htons(a);}
\DoxyCodeLine{}
\DoxyCodeLine{\textcolor{comment}{//将socket与该端口绑定}}
\DoxyCodeLine{\textcolor{keywordflow}{if} (bind(*sock, (\textcolor{keyword}{struct} sockaddr *)\&t\_data\_addr, \textcolor{keyword}{sizeof}(\textcolor{keyword}{struct} sockaddr)) < 0)\{}
\DoxyCodeLine{    perror(\textcolor{stringliteral}{"{}Bind error in create data socket:"{}});}
\DoxyCodeLine{\}}
\DoxyCodeLine{}
\DoxyCodeLine{\textcolor{comment}{//监听端口}}
\DoxyCodeLine{listen(*sock, 1);}
\DoxyCodeLine{}
\DoxyCodeLine{\textcolor{keywordflow}{return} t\_data\_addr;}
\DoxyCodeLine{}
\DoxyCodeLine{\textcolor{comment}{//cmd\_server.c : handle\_pasv}}
\DoxyCodeLine{\textcolor{comment}{//FTP响应，告知客户端开放的数据端口信息（这里进行了简化，省略了ip，客户端直接获取控制连接中的ip）}}
\DoxyCodeLine{\textcolor{keywordtype}{char} pasv\_msg[MSG\_INFO];}
\DoxyCodeLine{\textcolor{keywordtype}{char} port\_str[8];}
\DoxyCodeLine{\textcolor{keywordtype}{int} data\_sock;}
\DoxyCodeLine{\textcolor{keyword}{struct }sockaddr\_in user\_data\_addr;}
\DoxyCodeLine{user\_data\_addr = \mbox{\hyperlink{cmd__server_8c_a122d023237866867fcdf8ee05bf2c25c}{create\_data\_sock}}(\&data\_sock);}
\DoxyCodeLine{}
\DoxyCodeLine{\textcolor{keywordtype}{int} tmp\_port1;}
\DoxyCodeLine{\textcolor{keywordtype}{int} tmp\_port2;}
\DoxyCodeLine{tmp\_port1 = ntohs(user\_data\_addr.sin\_port) / 256;}
\DoxyCodeLine{tmp\_port2 = ntohs(user\_data\_addr.sin\_port) \% 256;}
\DoxyCodeLine{}
\DoxyCodeLine{}
\DoxyCodeLine{snprintf(port\_str, \textcolor{keyword}{sizeof}(port\_str), \textcolor{stringliteral}{"{}port,\%d,\%d"{}}, tmp\_port1, tmp\_port2);}
\DoxyCodeLine{snprintf(pasv\_msg, MSG\_INFO, \textcolor{stringliteral}{"{}227 Entering Passive Mode \%s.\(\backslash\)r\(\backslash\)n"{}}, port\_str);}
\DoxyCodeLine{\mbox{\hyperlink{ftp__server_8c_a80762bccbf172f3bcc3c6149c6c407c5}{send\_message}}(client\_sock, pasv\_msg, \textcolor{stringliteral}{"{}\(\backslash\)0"{}});}
\DoxyCodeLine{}
\DoxyCodeLine{\textcolor{comment}{//获取已建立的socket，数据连接建立。}}
\DoxyCodeLine{msg-\/>ftp\_data\_sock = accept(data\_sock, (\textcolor{keyword}{struct} sockaddr *)\&user\_data\_addr, (socklen\_t *)\&set);}

\end{DoxyCode}
\hypertarget{index_autotoc_md12}{}\doxysubsection{屏幕截图}\label{index_autotoc_md12}
\hypertarget{index_autotoc_md13}{}\doxysubsubsection{1、启动}\label{index_autotoc_md13}
\hypertarget{index_autotoc_md14}{}\doxysubsubsection{2、查看命令}\label{index_autotoc_md14}
\hypertarget{index_autotoc_md15}{}\doxysubsubsection{3、测试}\label{index_autotoc_md15}
\hypertarget{index_autotoc_md16}{}\doxysubsection{鲁棒性处理}\label{index_autotoc_md16}
本程序具有较高的鲁棒性，且实现了较好的功能，具体表现为：


\begin{DoxyEnumerate}
\item 错误指令执行help命令。
\item get检查不存在的文件，进行错误提示
\item get时检查本地是否存在同名文件并询问是否覆盖，否则退出
\item get可以进行向下的多目录级命令，如{\ttfamily get A/test.\+txt}
\item get检查是否带有参数，不带参数提示错误。
\item put检查不存在的文件，进行错误提示
\item put可以进行向下的多目录级命令，如{\ttfamily put A/test.\+txt}
\item put检查是否带有参数，不带参数提示错误。
\item mkdir检查是否带有参数，不带参数提示错误。
\item mkdir会检查当前目录下是否已存在同名文件夹，若存在提示错误
\item mkdir允许进行向下的多目录级命令，如{\ttfamily mkdir A/B}
\item mkdir时若无写入权限则提示错误。
\item rmdir检查是否带有参数，不带参数提示错误。
\item rmdir会检查目标文件夹是否存在，不存在提示错误。
\item rmdir会检查目标文件夹是否为空，不为空提示错误。
\item rmdir允许进行向下的多目录级命令，如{\ttfamily rmdir A/B}
\item delete检查是否带有参数，不带参数提示错误。
\item delete会检查目标文件是否存在，不存在提示错误。
\item delete允许进行向下的多目录级命令，如{\ttfamily delete A/\+B.\+txt}
\item cd允许进行多目录级的命令，如{\ttfamily cd A/B}，{\ttfamily cd ../A}，{\ttfamily cd ./A}。
\item cd检查是否带有参数，不带参数提示错误。
\item cd会检查目标文件夹是否存在，不存在提示错误。
\item ls无视参数直接显示当前目录列表
\item lss等与命令相近的错误指令能有效检测
\item pwd无视参数直接显示当前目录
\item pwds等与命令相近的错误指令能有效检测
\item 对于lpwd、lcd、lls等本地指令，同样具有相似鲁棒性。
\end{DoxyEnumerate}\hypertarget{index_autotoc_md17}{}\doxysubsection{API Doc}\label{index_autotoc_md17}
本项目使用\+Doxgen创建了html格式的文档，文档内的\+Files栏目下可查看各函数详细功能和参数的注解：   文档可在html目录下的index.\+html中查看，或者点这里快速跳转： \href{./html/index.html}{\texttt{ index}} 